class FetchRequest {
  constructor({ baseUrl = '', timeout = 1000 * 6 }) {
    this.baseUrl = baseUrl;
    this.timeout = timeout;
    this.controller = new AbortController();

    this.options = {
      url: '',
      method: 'get',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      body: undefined,
      referrer: '',
      referrerPolicy: '',
      mode: 'cors', // 请求模式
      credentials: '', // 是否发送cookie（跨域发送请设为 include）
      cache: 'default', // 缓存处理方式
      redirect: 'follow', // 指定 HTTP 跳转的处理方法
      integrity: '', // 指定一个哈希值检查res数据是否等于这个预先设定的哈希值
      keepalive: false, // 页面卸载时继续发送数据
      signal: this.controller.signal, // 指定一个 AbortSignal 实例，用于取消fetch请求
      params: {}, // get请求参数
      data: {}, // post请求参数
    };
  }

  getParamsStr(params) {
    const target = [];
    for (const key in params) {
      target.push(`${key}=${params[key]}`);
    }
    return target.length > 0 ? `?${target.join('&')}` : '';
  }

  request({ url, methods = 'GET', params = {}, data = {} }) {
    if (['GET', 'get'].includes(methods)) {
      // get请求 将参数放到url上
      url = `${this.baseURL}${url}${this.getParamsStr(params)}`;
    }

    const opts = { ...this.options, methods, params, data };
    if (['POST', 'post'].includes(methods)) {
      // post参数以json的方式传给后端
      opts.body = JSON.stringify(op.data);
    }

    return new Promise((resolve, reject) => {
      fetch(url, opts)
        .then((res) => {
          const json = res.json();
          if (res.ok && res.status >= 200 && res.status < 300) {
            resolve(json);
          } else {
            reject(res.statusText);
          }
        })
        .catch((err) => {
          reject(err);
        });
    });
  }

  get(opts = { method: 'GET', params: {}, url: '' }) {
    return this.request(opts);
  }

  post(opts = { method: 'POST', data: {}, url: '' }) {
    return this.request(opts);
  }

  // 服务端出现了问题请求将会一直处于pending的状态中，处理
  overtime() {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        this.controller.abort();
        reject();
      }, this.timeout);
    });
  }

  // 需要把超时的和之前的请求拼在一起，两个Promise并行执行，任何一个执行结束了，另一个也就不会执行了
  // 用Promise.race处理
  http(opts) {
    return Promise.race([this.request(opts), this.overtime()]);
  }
}
